#include <linux/err.h>
#include <linux/errno.h>
#include <linux/magic.h>
#include <linux/buffer_head.h>
#include <linux/stat.h>
#include <linux/log2.h>
#include "ext2.h"

void ext2_error (struct super_block * sb, const char * function,
		 const char * fmt, ...)
{
	printf("this is %s(): %d\r\r\n", __func__, __LINE__);
	// va_list args;
	// struct ext2_sb_info *sbi = EXT2_SB(sb);
	// struct ext2_super_block *es = sbi->s_es;

	// // if (!(sb->s_flags & MS_RDONLY)) {
	// // 	sbi->s_mount_state |= EXT2_ERROR_FS;
	// // 	es->s_state |= cpu_to_le16(EXT2_ERROR_FS);
	// // 	ext2_sync_super(sb, es);
	// // }

	// va_start(args, fmt);
	// printf(KERN_CRIT "EXT2-fs error (device %s): %s: ",sb->s_id, function);
	// vprintk(fmt, args);
	// printf("\r\n");
	// va_end(args);

	// if (test_opt(sb, ERRORS_PANIC))
	// 	panic("EXT2-fs panic from previous error\r\n");
	// if (test_opt(sb, ERRORS_RO)) {
	// 	printf("Remounting filesystem read-only\r\n");
	// 	sb->s_flags |= MS_RDONLY;
	// }
}

static struct inode *ext2_alloc_inode(struct super_block *sb)
{
	struct ext2_inode_info *ei;
	// ei = (struct ext2_inode_info *)kmem_cache_alloc(ext2_inode_cachep, GFP_KERNEL);
	ei = (struct ext2_inode_info *)kmalloc(sizeof(struct ext2_inode_info));
	if (!ei)
		return NULL;
	ei->i_block_alloc_info = NULL;
	ei->vfs_inode.i_version = 1;
	return &ei->vfs_inode;
}

void ext2_write_super(struct super_block *sb)
{
	// if (!(sb->s_flags & MS_RDONLY))
	// 	ext2_sync_fs(sb, 1);
	// else
		sb->s_dirt = 0;
}

static const struct super_operations ext2_sops = {
	.alloc_inode	= ext2_alloc_inode,
	// .destroy_inode	= ext2_destroy_inode,
	// .write_inode	= ext2_write_inode,
	// .delete_inode	= ext2_delete_inode,
	// .put_super	= ext2_put_super,
// 	.write_super	= ext2_write_super,
// 	.sync_fs	= ext2_sync_fs,
// 	.statfs		= ext2_statfs,
// 	.remount_fs	= ext2_remount,
// 	.clear_inode	= ext2_clear_inode,
// 	.show_options	= ext2_show_options,
// #ifdef CONFIG_QUOTA
// 	.quota_read	= ext2_quota_read,
// 	.quota_write	= ext2_quota_write,
// #endif
};

static int ext2_setup_super(struct super_block * sb,
			      struct ext2_super_block * es,
			      int read_only)
{
	int res = 0;
	struct ext2_sb_info *sbi = EXT2_SB(sb);

	if (le32_to_cpu(es->s_rev_level) > EXT2_MAX_SUPP_REV) {
		printf ("EXT2-fs warning: revision level too high, "
			"forcing read-only mode\r\n");
		res = MS_RDONLY;
	}
	if (read_only)
		return res;
	if (!(sbi->s_mount_state & EXT2_VALID_FS))
		printf ("EXT2-fs warning: mounting unchecked fs, "
			"running e2fsck is recommended\r\n");
	else if ((sbi->s_mount_state & EXT2_ERROR_FS))
		printf ("EXT2-fs warning: mounting fs with errors, "
			"running e2fsck is recommended\r\n");
	else if ((__s16) le16_to_cpu(es->s_max_mnt_count) >= 0 &&
		 le16_to_cpu(es->s_mnt_count) >=
		 (unsigned short) (__s16) le16_to_cpu(es->s_max_mnt_count))
		printf ("EXT2-fs warning: maximal mount count reached, "
			"running e2fsck is recommended\r\n");
	// else if (le32_to_cpu(es->s_checkinterval) &&
	// 	(le32_to_cpu(es->s_lastcheck) + le32_to_cpu(es->s_checkinterval) <= get_seconds()))
	// 	printf ("EXT2-fs warning: checktime reached, "
	// 		"running e2fsck is recommended\r\n");
	if (!le16_to_cpu(es->s_max_mnt_count))
		es->s_max_mnt_count = cpu_to_le16(EXT2_DFL_MAX_MNT_COUNT);
	le16_add_cpu(&es->s_mnt_count, 1);
	ext2_write_super(sb);
	if (test_opt (sb, DEBUG))
		printf ("[EXT II FS %s, %s, bs=%lu, fs=%lu, gc=%lu, "
			"bpg=%lu, ipg=%lu, mo=%04lx]\r\n",
			EXT2FS_VERSION, EXT2FS_DATE, sb->s_blocksize,
			sbi->s_frag_size,
			sbi->s_groups_count,
			EXT2_BLOCKS_PER_GROUP(sb),
			EXT2_INODES_PER_GROUP(sb),
			sbi->s_mount_opt);
	return res;
}

static int ext2_check_descriptors(struct super_block *sb)
{
	int i;
	struct ext2_sb_info *sbi = EXT2_SB(sb);

	// ext2_debug ("Checking group descriptors");

	for (i = 0; i < sbi->s_groups_count; i++) {
		struct ext2_group_desc *gdp = ext2_get_group_desc(sb, i, NULL);
		ext2_fsblk_t first_block = ext2_group_first_block_no(sb, i);
		ext2_fsblk_t last_block;

		if (i == sbi->s_groups_count - 1)
			last_block = le32_to_cpu(sbi->s_es->s_blocks_count) - 1;
		else
			last_block = first_block +
				(EXT2_BLOCKS_PER_GROUP(sb) - 1);

		if (le32_to_cpu(gdp->bg_block_bitmap) < first_block ||
		    le32_to_cpu(gdp->bg_block_bitmap) > last_block)
		{
			ext2_error (sb, "ext2_check_descriptors",
				    "Block bitmap for group %d"
				    " not in group (block %lu)!",
				    i, (unsigned long) le32_to_cpu(gdp->bg_block_bitmap));
			return 0;
		}
		if (le32_to_cpu(gdp->bg_inode_bitmap) < first_block ||
		    le32_to_cpu(gdp->bg_inode_bitmap) > last_block)
		{
			ext2_error (sb, "ext2_check_descriptors",
				    "Inode bitmap for group %d"
				    " not in group (block %lu)!",
				    i, (unsigned long) le32_to_cpu(gdp->bg_inode_bitmap));
			return 0;
		}
		if (le32_to_cpu(gdp->bg_inode_table) < first_block ||
		    le32_to_cpu(gdp->bg_inode_table) + sbi->s_itb_per_group - 1 >
		    last_block)
		{
			ext2_error (sb, "ext2_check_descriptors",
				    "Inode table for group %d"
				    " not in group (block %lu)!",
				    i, (unsigned long) le32_to_cpu(gdp->bg_inode_table));
			return 0;
		}
	}
	return 1;
}

static loff_t ext2_max_size(int bits)
{
	loff_t res = EXT2_NDIR_BLOCKS;
	int meta_blocks;
	loff_t upper_limit;

	/* This is calculated to be the largest file size for a
	 * dense, file such that the total number of
	 * sectors in the file, including data and all indirect blocks,
	 * does not exceed 2^32 -1
	 * __u32 i_blocks representing the total number of
	 * 512 bytes blocks of the file
	 */
	upper_limit = (1LL << 32) - 1;

	/* total blocks in file system block size */
	upper_limit >>= (bits - 9);


	/* indirect blocks */
	meta_blocks = 1;
	/* double indirect blocks */
	meta_blocks += 1 + (1LL << (bits-2));
	/* tripple indirect blocks */
	meta_blocks += 1 + (1LL << (bits-2)) + (1LL << (2*(bits-2)));

	upper_limit -= meta_blocks;
	upper_limit <<= bits;

	res += 1LL << (bits-2);
	res += 1LL << (2*(bits-2));
	res += 1LL << (3*(bits-2));
	res <<= bits;
	if (res > upper_limit)
		res = upper_limit;

	if (res > MAX_LFS_FILESIZE)
		res = MAX_LFS_FILESIZE;

	return res;
}

static unsigned long descriptor_loc(struct super_block *sb,
				    unsigned long logic_sb_block,
				    int nr)
{
	struct ext2_sb_info *sbi = EXT2_SB(sb);
	unsigned long bg, first_meta_bg;
	int has_super = 0;
	
	first_meta_bg = le32_to_cpu(sbi->s_es->s_first_meta_bg);

	if (!EXT2_HAS_INCOMPAT_FEATURE(sb, EXT2_FEATURE_INCOMPAT_META_BG) ||
	    nr < first_meta_bg)
		return (logic_sb_block + nr + 1);
	bg = sbi->s_desc_per_block * nr;
	if (ext2_bg_has_super(sb, bg))
		has_super = 1;

	return ext2_group_first_block_no(sb, bg) + has_super;
}

#define EXT2_START_SECTOR_ON_SDCARD		411648

static int ext2_fill_super(struct super_block *sb, void *data, int silent)
{
	struct buffer_head * bh;
	struct ext2_sb_info * sbi;
	struct ext2_super_block * es;
	struct inode *root;
	unsigned long block;
	// unsigned long sb_block = get_sb_block(&data);
	unsigned long sb_block = 1;
	unsigned long logic_sb_block;
	unsigned long offset = 0;
	unsigned long def_mount_opts;
	long ret = -EINVAL;
	int blocksize = BLOCK_SIZE;
	int db_count;
	int i, j;
	__le32 features;
	int err;
	char *sd_buf = (char *)kmalloc(BLOCK_SIZE);

	printf("this is %s(): %d\r\r\n", __func__, __LINE__);

	// sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
	sbi = kzalloc(sizeof(*sbi));
	if (!sbi)
		return -ENOMEM;

	// sbi->s_blockgroup_lock =
	// 	kzalloc(sizeof(struct blockgroup_lock), GFP_KERNEL);
	// if (!sbi->s_blockgroup_lock) {
	// 	kfree(sbi);
	// 	return -ENOMEM;
	// }
	sb->s_fs_info = sbi;
	sbi->s_sb_block = sb_block;

	/*
	 * See what the current blocksize for the device is, and
	 * use that as the blocksize.  Otherwise (or if the blocksize
	 * is smaller than the default) use the default.
	 * This is important for devices that have a hardware
	 * sectorsize that is larger than the default.
	 */
	blocksize = sb_min_blocksize(sb, BLOCK_SIZE);
	// blocksize = 512;
	if (!blocksize) {
		printf ("EXT2-fs: unable to set blocksize\r\n");
		goto failed_sbi;
	}

	/*
	 * If the superblock doesn't start on a hardware sector boundary,
	 * calculate the offset.  
	 */
	if (blocksize != BLOCK_SIZE) {
		logic_sb_block = (sb_block*BLOCK_SIZE) / blocksize;
		offset = (sb_block*BLOCK_SIZE) % blocksize;
	} else {
		logic_sb_block = sb_block;
	}

#if 1
	if (!(bh = sb_bread(sb, logic_sb_block))) {
		printf ("EXT2-fs: unable to read superblock\r\n");
		goto failed_sbi;
	}
	/*
	 * Note: s_es must be initialized as soon as possible because
	 *       some ext2 macro-instructions depend on its value
	 */
	es = (struct ext2_super_block *) (((char *)bh->b_data) + offset);
#else
	printf("this is %s(): %d >>> logic_sb_block = %d\r\r\n", __func__, __LINE__, logic_sb_block);
	printf("this is %s(): %d >>> offset = %d\r\r\n", __func__, __LINE__, offset);
	sd_read_sector((unsigned int *)sd_buf, EXT2_START_SECTOR_ON_SDCARD + logic_sb_block, 1);
	/*
	 * Note: s_es must be initialized as soon as possible because
	 *       some ext2 macro-instructions depend on its value
	 */
	es = (struct ext2_super_block *) (((char *)sd_buf) + offset);
#endif
#if 1
	printf("this is %s(): %d >>> es->s_inodes_count = %d\r\r\n", __func__, __LINE__, es->s_inodes_count);
	printf("this is %s(): %d >>> es->s_blocks_count = %d\r\r\n", __func__, __LINE__, es->s_blocks_count);
	printf("this is %s(): %d >>> es->s_r_blocks_count = %d\r\r\n", __func__, __LINE__, es->s_r_blocks_count);
	printf("this is %s(): %d >>> es->s_free_blocks_count = %d\r\r\n", __func__, __LINE__, es->s_free_blocks_count);
	printf("this is %s(): %d >>> es->s_free_inodes_count = %d\r\r\n", __func__, __LINE__, es->s_free_inodes_count);
	printf("this is %s(): %d >>> es->s_first_data_block = %d\r\r\n", __func__, __LINE__, es->s_first_data_block);
	printf("this is %s(): %d >>> es->s_magic = %x\r\r\n", __func__, __LINE__, es->s_magic);
	printf("this is %s(): %d >>> es->s_log_block_size = %x\r\r\n", __func__, __LINE__, es->s_log_block_size);
	// while (1);
#endif

	sbi->s_es = es;
	sb->s_magic = le16_to_cpu(es->s_magic);

	if (sb->s_magic != EXT2_SUPER_MAGIC)
		goto cantfind_ext2;

	printf("this is %s(): %d\r\r\n", __func__, __LINE__);

	/* Set defaults before we parse the mount options */
// 	def_mount_opts = le32_to_cpu(es->s_default_mount_opts);
// 	if (def_mount_opts & EXT2_DEFM_DEBUG)
// 		set_opt(sbi->s_mount_opt, DEBUG);
// 	if (def_mount_opts & EXT2_DEFM_BSDGROUPS)
// 		set_opt(sbi->s_mount_opt, GRPID);
// 	if (def_mount_opts & EXT2_DEFM_UID16)
// 		set_opt(sbi->s_mount_opt, NO_UID32);
// #ifdef CONFIG_EXT2_FS_XATTR
// 	if (def_mount_opts & EXT2_DEFM_XATTR_USER)
// 		set_opt(sbi->s_mount_opt, XATTR_USER);
// #endif
// #ifdef CONFIG_EXT2_FS_POSIX_ACL
// 	if (def_mount_opts & EXT2_DEFM_ACL)
// 		set_opt(sbi->s_mount_opt, POSIX_ACL);
// #endif
	
	if (le16_to_cpu(sbi->s_es->s_errors) == EXT2_ERRORS_PANIC)
		set_opt(sbi->s_mount_opt, ERRORS_PANIC);
	else if (le16_to_cpu(sbi->s_es->s_errors) == EXT2_ERRORS_CONTINUE)
		set_opt(sbi->s_mount_opt, ERRORS_CONT);
	else
		set_opt(sbi->s_mount_opt, ERRORS_RO);

	sbi->s_resuid = le16_to_cpu(es->s_def_resuid);
	sbi->s_resgid = le16_to_cpu(es->s_def_resgid);
	
	set_opt(sbi->s_mount_opt, RESERVATION);

	// if (!parse_options ((char *) data, sbi))
	// 	goto failed_mount;

	sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
		((EXT2_SB(sb)->s_mount_opt & EXT2_MOUNT_POSIX_ACL) ?
		 MS_POSIXACL : 0);

	// ext2_xip_verify_sb(sb); /* see if bdev supports xip, unset
	// 			    EXT2_MOUNT_XIP if not */

	// if (le32_to_cpu(es->s_rev_level) == EXT2_GOOD_OLD_REV &&
	//     (EXT2_HAS_COMPAT_FEATURE(sb, ~0U) ||
	//      EXT2_HAS_RO_COMPAT_FEATURE(sb, ~0U) ||
	//      EXT2_HAS_INCOMPAT_FEATURE(sb, ~0U)))
	// 	printf("EXT2-fs warning: feature flags set on rev 0 fs, "
	// 	       "running e2fsck is recommended\r\n");
	/*
	 * Check feature flags regardless of the revision level, since we
	 * previously didn't change the revision level when setting the flags,
	 * so there is a chance incompat flags are set on a rev 0 filesystem.
	 */
	// features = EXT2_HAS_INCOMPAT_FEATURE(sb, ~EXT2_FEATURE_INCOMPAT_SUPP);
	// if (features) {
	// 	printf("EXT2-fs: %s: couldn't mount because of "
	// 	       "unsupported optional features (%x).\r\n",
	// 	       sb->s_id, le32_to_cpu(features));
	// 	goto failed_mount;
	// }
	// if (!(sb->s_flags & MS_RDONLY) &&
	//     (features = EXT2_HAS_RO_COMPAT_FEATURE(sb, ~EXT2_FEATURE_RO_COMPAT_SUPP))){
	// 	printf("EXT2-fs: %s: couldn't mount RDWR because of "
	// 	       "unsupported optional features (%x).\r\n",
	// 	       sb->s_id, le32_to_cpu(features));
	// 	goto failed_mount;
	// }

	blocksize = BLOCK_SIZE << le32_to_cpu(sbi->s_es->s_log_block_size);
	printf("this is %s(): %d >>> sbi->s_es->s_log_block_size = %d\r\r\n", __func__, __LINE__, sbi->s_es->s_log_block_size);
	printf("this is %s(): %d >>> blocksize = %d\r\r\n", __func__, __LINE__, blocksize);
	printf("this is %s(): %d >>> sb->s_blocksize = %d\r\r\n", __func__, __LINE__, sb->s_blocksize);

	// if (ext2_use_xip(sb) && blocksize != PAGE_SIZE) {
	// 	if (!silent)
	// 		printf("XIP: Unsupported blocksize\r\n");
	// 	goto failed_mount;
	// }

	/* If the blocksize doesn't match, re-read the thing.. */
	if (sb->s_blocksize != blocksize) {
		printf("this is %s(): %d\r\r\n", __func__, __LINE__);
		brelse(bh);

		if (!sb_set_blocksize(sb, blocksize)) {
			printf("EXT2-fs: blocksize too small for device.\r\n");
			goto failed_sbi;
		}

		logic_sb_block = (sb_block*BLOCK_SIZE) / blocksize;
		offset = (sb_block*BLOCK_SIZE) % blocksize;
		bh = sb_bread(sb, logic_sb_block);
		if(!bh) {
			printf("EXT2-fs: Couldn't read superblock on "
			       "2nd try.\r\n");
			goto failed_sbi;
		}
		// sd_read_sector((unsigned int *)sd_buf, EXT2_START_SECTOR_ON_SDCARD + logic_sb_block, 1);
		es = (struct ext2_super_block *) (((char *)bh->b_data) + offset);
		// es = (struct ext2_super_block *) (((char *)sd_buf) + offset);
		sbi->s_es = es;
		if (es->s_magic != cpu_to_le16(EXT2_SUPER_MAGIC)) {
			printf ("EXT2-fs: Magic mismatch, very weird !\r\n");
			goto failed_mount;
		}
	}

	sb->s_maxbytes = ext2_max_size(sb->s_blocksize_bits);

	if (le32_to_cpu(es->s_rev_level) == EXT2_GOOD_OLD_REV) {
		sbi->s_inode_size = EXT2_GOOD_OLD_INODE_SIZE;
		sbi->s_first_ino = EXT2_GOOD_OLD_FIRST_INO;
	} else {
		sbi->s_inode_size = le16_to_cpu(es->s_inode_size);
		sbi->s_first_ino = le32_to_cpu(es->s_first_ino);
		if ((sbi->s_inode_size < EXT2_GOOD_OLD_INODE_SIZE) ||
		    !is_power_of_2(sbi->s_inode_size) ||
		    (sbi->s_inode_size > blocksize)) {
			printf ("EXT2-fs: unsupported inode size: %d\r\n",
				sbi->s_inode_size);
			goto failed_mount;
		}
	}

	sbi->s_frag_size = EXT2_MIN_FRAG_SIZE <<
				   le32_to_cpu(es->s_log_frag_size);
	if (sbi->s_frag_size == 0)
		goto cantfind_ext2;
	sbi->s_frags_per_block = sb->s_blocksize / sbi->s_frag_size;

	sbi->s_blocks_per_group = le32_to_cpu(es->s_blocks_per_group);
	sbi->s_frags_per_group = le32_to_cpu(es->s_frags_per_group);
	sbi->s_inodes_per_group = le32_to_cpu(es->s_inodes_per_group);

	if (EXT2_INODE_SIZE(sb) == 0)
		goto cantfind_ext2;
	printf("this is %s(): %d\r\r\n", __func__, __LINE__);
	sbi->s_inodes_per_block = sb->s_blocksize / EXT2_INODE_SIZE(sb);
	if (sbi->s_inodes_per_block == 0 || sbi->s_inodes_per_group == 0)
		goto cantfind_ext2;
	printf("this is %s(): %d\r\r\n", __func__, __LINE__);
	sbi->s_itb_per_group = sbi->s_inodes_per_group /
					sbi->s_inodes_per_block;
	sbi->s_desc_per_block = sb->s_blocksize /
					sizeof (struct ext2_group_desc);
	sbi->s_sbh = bh;
	sbi->s_mount_state = le16_to_cpu(es->s_state);
	sbi->s_addr_per_block_bits =
		ilog2 (EXT2_ADDR_PER_BLOCK(sb));
	sbi->s_desc_per_block_bits =
		ilog2 (EXT2_DESC_PER_BLOCK(sb));

	if (sb->s_magic != EXT2_SUPER_MAGIC)
		goto cantfind_ext2;
	printf("this is %s(): %d\r\r\n", __func__, __LINE__);

	if (sb->s_blocksize != bh->b_size) {
		printf("this is %s(): %d\r\r\n", __func__, __LINE__);
		if (!silent)
			printf ("VFS: Unsupported blocksize on dev "
				"%s.\r\n", sb->s_id);
		goto failed_mount;
	}

	if (sb->s_blocksize != sbi->s_frag_size) {
		printf ("EXT2-fs: fragsize %lu != blocksize %lu (not supported yet)\r\n",
			sbi->s_frag_size, sb->s_blocksize);
		goto failed_mount;
	}

	if (sbi->s_blocks_per_group > sb->s_blocksize * 8) {
		printf ("EXT2-fs: #blocks per group too big: %lu\r\n",
			sbi->s_blocks_per_group);
		goto failed_mount;
	}
	if (sbi->s_frags_per_group > sb->s_blocksize * 8) {
		printf ("EXT2-fs: #fragments per group too big: %lu\r\n",
			sbi->s_frags_per_group);
		goto failed_mount;
	}
	if (sbi->s_inodes_per_group > sb->s_blocksize * 8) {
		printf ("EXT2-fs: #inodes per group too big: %lu\r\n",
			sbi->s_inodes_per_group);
		goto failed_mount;
	}

	if (EXT2_BLOCKS_PER_GROUP(sb) == 0)
		goto cantfind_ext2;
	printf("this is %s(): %d\r\r\n", __func__, __LINE__);
 	sbi->s_groups_count = ((le32_to_cpu(es->s_blocks_count) -
 				le32_to_cpu(es->s_first_data_block) - 1)
 					/ EXT2_BLOCKS_PER_GROUP(sb)) + 1;
	db_count = (sbi->s_groups_count + EXT2_DESC_PER_BLOCK(sb) - 1) /
		   EXT2_DESC_PER_BLOCK(sb);
	// sbi->s_group_desc = kmalloc (db_count * sizeof (struct buffer_head *), GFP_KERNEL);
	sbi->s_group_desc = kmalloc(db_count * sizeof (struct buffer_head *));
	if (sbi->s_group_desc == NULL) {
		printf ("EXT2-fs: not enough memory\r\n");
		goto failed_mount;
	}
	// bgl_lock_init(sbi->s_blockgroup_lock);
	// sbi->s_debts = kcalloc(sbi->s_groups_count, sizeof(*sbi->s_debts), GFP_KERNEL);
	sbi->s_debts = kmalloc(sbi->s_groups_count, sizeof(*sbi->s_debts));
	if (!sbi->s_debts) {
		printf ("EXT2-fs: not enough memory\r\n");
		goto failed_mount_group_desc;
	}
	for (i = 0; i < db_count; i++) {
		printf("this is %s(): %d\r\r\n", __func__, __LINE__);
		block = descriptor_loc(sb, logic_sb_block, i);
		sbi->s_group_desc[i] = sb_bread(sb, block);
		// sd_read_sector((unsigned int *)sd_buf, EXT2_START_SECTOR_ON_SDCARD + block, 1);
		if (!sbi->s_group_desc[i]) {
			for (j = 0; j < i; j++)
				brelse (sbi->s_group_desc[j]);
			printf ("EXT2-fs: unable to read group descriptors\r\n");
			goto failed_mount_group_desc;
		}
	}
	if (!ext2_check_descriptors (sb)) {
		printf ("EXT2-fs: group descriptors corrupted!\r\n");
		goto failed_mount2;
	}
	sbi->s_gdb_count = db_count;
	// get_random_bytes(&sbi->s_next_generation, sizeof(u32));
	sbi->s_next_generation = 66;
	// spin_lock_init(&sbi->s_next_gen_lock);

	/* per fileystem reservation list head & lock */
	// spin_lock_init(&sbi->s_rsv_window_lock);
	sbi->s_rsv_window_root = RB_ROOT;
	/*
	 * Add a single, static dummy reservation to the start of the
	 * reservation window list --- it gives us a placeholder for
	 * append-at-start-of-list which makes the allocation logic
	 * _much_ simpler.
	 */
	// sbi->s_rsv_window_head.rsv_start = EXT2_RESERVE_WINDOW_NOT_ALLOCATED;
	// sbi->s_rsv_window_head.rsv_end = EXT2_RESERVE_WINDOW_NOT_ALLOCATED;
	// sbi->s_rsv_window_head.rsv_alloc_hit = 0;
	// sbi->s_rsv_window_head.rsv_goal_size = 0;
	// ext2_rsv_window_add(sb, &sbi->s_rsv_window_head);

	// err = percpu_counter_init(&sbi->s_freeblocks_counter,
	// 			ext2_count_free_blocks(sb));
	// if (!err) {
	// 	err = percpu_counter_init(&sbi->s_freeinodes_counter,
	// 			ext2_count_free_inodes(sb));
	// }
	// if (!err) {
	// 	err = percpu_counter_init(&sbi->s_dirs_counter,
	// 			ext2_count_dirs(sb));
	// }
	// if (err) {
	// 	printf("EXT2-fs: insufficient memory\r\n");
	// 	goto failed_mount3;
	// }
	/*
	 * set up enough so that it can read an inode
	 */
	sb->s_op = &ext2_sops;
	// sb->s_export_op = &ext2_export_ops;
	// sb->s_xattr = ext2_xattr_handlers;
	printf("this is %s(): %d\r\r\n", __func__, __LINE__);
	root = ext2_iget(sb, EXT2_ROOT_INO);
	printf("this is %s(): %d\r\r\n", __func__, __LINE__);
	if (IS_ERR(root)) {
		ret = PTR_ERR(root);
		goto failed_mount3;
	}
	printf("this is %s(): %d\r\r\n", __func__, __LINE__);
	if (!S_ISDIR(root->i_mode) || !root->i_blocks || !root->i_size) {
		// iput(root);
		printf("EXT2-fs: corrupt root inode, run e2fsck\r\n");
		goto failed_mount3;
	}

	printf("this is %s(): %d\r\r\n", __func__, __LINE__);
	sb->s_root = d_alloc_root(root);
	printf("this is %s(): %d\r\r\n", __func__, __LINE__);
	if (!sb->s_root) {
		// iput(root);
		printf("EXT2-fs: get root inode failed\r\n");
		ret = -ENOMEM;
		goto failed_mount3;
	}
	if (EXT2_HAS_COMPAT_FEATURE(sb, EXT3_FEATURE_COMPAT_HAS_JOURNAL))
		// ext2_warning(sb, __func__,
		// 	"mounting ext3 filesystem as ext2");
		printf("mounting ext3 filesystem as ext2");
	ext2_setup_super(sb, es, sb->s_flags & MS_RDONLY);
	printf("this is %s(): %d\r\r\n", __func__, __LINE__);
	return 0;

cantfind_ext2:
	printf("this is %s(): %d\r\r\n", __func__, __LINE__);
	if (!silent)
		printf("VFS: Can't find an ext2 filesystem on dev %s.\r\n",
		       sb->s_id);
	goto failed_mount;
failed_mount3:
	printf("this is %s(): %d\r\r\n", __func__, __LINE__);
	// percpu_counter_destroy(&sbi->s_freeblocks_counter);
	// percpu_counter_destroy(&sbi->s_freeinodes_counter);
	// percpu_counter_destroy(&sbi->s_dirs_counter);
failed_mount2:
	printf("this is %s(): %d\r\r\n", __func__, __LINE__);
	for (i = 0; i < db_count; i++)
		brelse(sbi->s_group_desc[i]);
failed_mount_group_desc:
	printf("this is %s(): %d\r\r\n", __func__, __LINE__);
	kfree(sbi->s_group_desc);
	kfree(sbi->s_debts);
failed_mount:
	printf("this is %s(): %d\r\r\n", __func__, __LINE__);
	brelse(bh);
failed_sbi:
	printf("this is %s(): %d\r\r\n", __func__, __LINE__);
	sb->s_fs_info = NULL;
	// kfree(sbi->s_blockgroup_lock);
	kfree(sbi);
	printf("this is %s(): %d\r\r\n", __func__, __LINE__);
	return ret;
}

static int ext2_get_sb(struct file_system_type *fs_type,
	int flags, const char *dev_name, void *data, struct vfsmount *mnt)
{
	printf("this is %s(): %d\r\r\n", __func__, __LINE__);
	return get_sb_bdev(fs_type, flags, dev_name, data, ext2_fill_super, mnt);
}

static struct file_system_type ext2_fs_type = {
	// .owner		= THIS_MODULE,
	.name		= "ext2",
	.get_sb		= ext2_get_sb,
	// .kill_sb	= kill_block_super,
	// .fs_flags	= FS_REQUIRES_DEV,
};

int init_ext2_fs(void)
{
	int err;
	// int err = init_ext2_xattr();
	// if (err)
	// 	return err;
	// err = init_inodecache();
	// if (err)
	// 	goto out1;
    err = register_filesystem(&ext2_fs_type);
	if (err)
		goto out;
	return 0;
out:
	// destroy_inodecache();
out1:
	// exit_ext2_xattr();
	return err;
}

static void exit_ext2_fs(void)
{
	// unregister_filesystem(&ext2_fs_type);
	// destroy_inodecache();
	// exit_ext2_xattr();
}
